package sql

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/spf13/cast"

	"vitess.io/vitess/go/sqltypes"
	querypb "vitess.io/vitess/go/vt/proto/query"
)

// Column is the definition of a table column.
// As SQL:2016 puts it:
//   A column is a named component of a table. It has a data type, a default,
//   and a nullability characteristic.
type Column struct {
	// Name is the name of the column.
	Name string
	// Type is the data type of the column.
	Type querypb.Type
	// Default contains the default value of the column or nil if it was not explicitly defined. A nil instance is valid, thus calls do not error.
	Default sqltypes.Value
	// AutoIncrement is true if the column auto-increments.
	AutoIncrement bool
	// Nullable is true if the column can contain NULL values, or false
	// otherwise.
	Nullable bool
	// Source is the name of the table this column came from.
	Source string
	// PrimaryKey is true if the column is part of the primary key for its table.
	PrimaryKey bool
	// Comment contains the string comment for this column.
	Comment string
	// Extra contains any additional information to put in the `extra` column under `information_schema.columns`.
	Extra string
}

// Check ensures the value is correct for this column.
func (c *Column) Check(v interface{}) bool {
	if v == nil {
		return c.Nullable
	}
	switch v.(type) {
	case int, int64:
		return c.Type == sqltypes.Int64
	case string:
		return (c.Type == sqltypes.VarChar || c.Type == sqltypes.Text)
	case int32:
		return c.Type == sqltypes.Int32
	case int8:
		return c.Type == sqltypes.Int8
	case float64:
		return c.Type == sqltypes.Float64
	case float32:
		return c.Type == sqltypes.Float32
	}
	return false
}

func (c *Column) ToBytes(val interface{}) []byte {
	switch c.Type {
	case sqltypes.Text, sqltypes.VarChar, sqltypes.Char:
		return []byte(cast.ToString(val))
	case sqltypes.Int64, sqltypes.Int32, sqltypes.Int16, sqltypes.Int8:
		return []byte(cast.ToString(val))
	case sqltypes.Uint64, sqltypes.Uint32, sqltypes.Uint24, sqltypes.Uint16, sqltypes.Uint8:
		return []byte(cast.ToString(val))
		//x := cast.ToUint8(val)
		//bytesBuffer := bytes.NewBuffer([]byte{})
		//binary.Write(bytesBuffer, binary.BigEndian, x)
		//return bytesBuffer.Bytes()
	}
	return nil
}

// Equals checks whether two columns are equal.
func (c *Column) Equals(c2 *Column) bool {
	return c.Name == c2.Name &&
		c.Source == c2.Source &&
		c.Nullable == c2.Nullable &&
		reflect.DeepEqual(c.Default, c2.Default) &&
		reflect.DeepEqual(c.Type, c2.Type)
}

func (c *Column) DebugString() string {
	sb := strings.Builder{}
	sb.WriteString("Name: ")
	sb.WriteString(c.Name)
	sb.WriteString(", ")
	sb.WriteString("Source: ")
	sb.WriteString(c.Source)
	sb.WriteString(", ")
	sb.WriteString("Type: ")
	sb.WriteString(c.Type.String())
	sb.WriteString(", ")
	sb.WriteString("PrimaryKey: ")
	sb.WriteString(fmt.Sprintf("%v", c.PrimaryKey))
	sb.WriteString(", ")
	sb.WriteString("Nullable: ")
	sb.WriteString(fmt.Sprintf("%v", c.Nullable))
	sb.WriteString(", ")
	sb.WriteString("Comment: ")
	sb.WriteString(c.Comment)
	sb.WriteString(", ")
	sb.WriteString("Default: ")
	sb.WriteString(c.Default.String())
	sb.WriteString(", ")
	sb.WriteString("AutoIncrement: ")
	sb.WriteString(fmt.Sprintf("%v", c.AutoIncrement))
	sb.WriteString(", ")
	sb.WriteString("Extra: ")
	sb.WriteString(c.Extra)

	return sb.String()
}
